use loco_rs::prelude::*;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

pub use super::_entities::customer_groups::{self, ActiveModel, Entity, Model};

#[derive(Debug, Deserialize, Serialize)]
pub struct CreateParams {
    pub level: i32,
    pub mini_cost: Decimal,
    pub discount_rate: Decimal,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct UpdateParams {
    pub level: Option<i32>,
    pub mini_cost: Option<Decimal>,
    pub discount_rate: Option<Decimal>,
}

#[async_trait::async_trait]
impl ActiveModelBehavior for super::_entities::customer_groups::ActiveModel {
    async fn before_save<C>(self, _db: &C, insert: bool) -> Result<Self, DbErr>
    where
        C: ConnectionTrait,
    {
        if insert {
            let mut this = self;
            this.id = ActiveValue::Set(Uuid::new_v4().to_string());
            Ok(this)
        } else {
            Ok(self)
        }
    }
}

impl Model {
    pub async fn create(db: &DatabaseConnection, params: CreateParams) -> ModelResult<Self> {
        let active_model = ActiveModel {
            level: ActiveValue::Set(params.level),
            mini_cost: ActiveValue::Set(params.mini_cost),
            discount_rate: ActiveValue::Set(params.discount_rate),
            ..Default::default()
        };

        let model = active_model.insert(db).await?;
        Ok(model)
    }

    pub async fn update_by_id(
        db: &DatabaseConnection,
        id: &str,
        params: UpdateParams,
    ) -> ModelResult<Self> {
        let group = Self::find_by_id(db, id).await?;
        let mut active_model: ActiveModel = group.into();

        if let Some(level) = params.level {
            active_model.level = ActiveValue::Set(level);
        }

        if let Some(mini_cost) = params.mini_cost {
            active_model.mini_cost = ActiveValue::Set(mini_cost);
        }

        if let Some(discount_rate) = params.discount_rate {
            active_model.discount_rate = ActiveValue::Set(discount_rate);
        }

        let model = active_model.update(db).await?;
        Ok(model)
    }

    pub async fn find_by_id(db: &DatabaseConnection, id: &str) -> ModelResult<Self> {
        let group = Entity::find_by_id(id).one(db).await?;
        group.ok_or_else(|| ModelError::EntityNotFound)
    }

    pub async fn delete_by_id(db: &DatabaseConnection, id: &str) -> ModelResult<()> {
        let group = Self::find_by_id(db, id).await?;
        let _result = Entity::delete_by_id(group.id).exec(db).await?;
        Ok(())
    }

    pub async fn find_by_level(db: &DatabaseConnection, level: i32) -> ModelResult<Self> {
        let group = Entity::find()
            .filter(customer_groups::Column::Level.eq(level))
            .one(db)
            .await?;
        group.ok_or_else(|| ModelError::EntityNotFound)
    }
}
